import os, sys
import json
import time
import threading
import subprocess
import shutil
import numpy as np
from PyQt5 import QtWidgets, QtGui
from PyQt5.QtWidgets import QLabel, QLineEdit, QSpacerItem, QWidget, QDialog, QVBoxLayout, QPushButton
from PyQt5.QtGui import QRegExpValidator
from PyQt5.QtCore import QRegExp, pyqtSignal, QTimer, Qt

from UI.home import Ui_MainWindow as Ui_MainWindow_raw
from Tools.scan_ssh_port import scan_all_ssh_port, get_all_local_ip


"""
重要说明:
app/version.json文件中不能有\r
冒号前后不能有空格
"""

Version = "V1.0.0"
DownloadPath = "download"
DatabaseFile = "record.db"
HintBlink = 0.4  # 0.4 second
HintText = f"""
导出xlsx
"""


class NpEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        if isinstance(obj, np.floating):
            return float(obj)
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        return json.JSONEncoder.default(self, obj)


def readJson(file):
    with open(file, "r+") as f:
        data = f.read()
    return json.loads(data)


class CustomDialog(QDialog):
    def __init__(self, text_list, parent=None):
        super(CustomDialog, self).__init__(parent)

        font_size = 16

        # 设置窗口标志，禁用标题栏
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        # self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)

        self.setStyleSheet("QDialog { border: 2px solid rgb(21,255,79); background-color: rgb(39,44,59)}")

        # 设置窗口标题和大小
        # self.setWindowTitle("目标IP清单")
        # self.resize(200, 300)

        # 创建布局
        layout = QVBoxLayout()
        layout.setAlignment(Qt.AlignCenter)

        # 创建一个标签
        label = QLabel("目标IP清单如下:\n", self)
        label.setStyleSheet(f"QLabel {{ font-size: {font_size}px; color:rgb(255,255,255) }}")
        layout.addWidget(label)

        # 创建一个标签
        text = ""
        for _text in text_list:
            text += _text + "\n"
        label = QLabel(text, self)
        label.setStyleSheet(f"QLabel {{ font-size: {font_size}px; color:rgb(255,255,255) }}")
        layout.addWidget(label)

        # 创建一个标签
        label = QLabel("将自动填入第一个IP地址\n", self)
        label.setStyleSheet(f"QLabel {{ font-size: {font_size}px; color:rgb(255,255,255) }}")
        layout.addWidget(label)

        # 创建一个按钮
        button = QPushButton("确认", self)
        button.setMaximumWidth(180)
        button.setStyleSheet(f"QPushButton {{ font-size: 16px }}")
        button.clicked.connect(self.accept)  # 当点击按钮时，接受对话框
        layout.addWidget(button)
        layout.setAlignment(button, Qt.AlignCenter)

        # 设置布局
        self.setLayout(layout)


class Ui_MainWindow(Ui_MainWindow_raw, QWidget):
    progressBar_signal = pyqtSignal(float)
    progressBar_show_signal = pyqtSignal(bool)
    button_ctrl_signal = pyqtSignal(bool)
    dialog_signal = pyqtSignal(list)
    set_address_signal = pyqtSignal(str)

    # 内容文字, 颜色(rgb(21,255,79) 或者 green, red, yellow...), 闪烁次数(0为不闪烁), 时间间隔
    label_hint_signal = pyqtSignal(str, str, int, float)

    def __init__(self):
        super().__init__()
        folder_path = DownloadPath
        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        MainWindow.setFixedSize(MainWindow.width(), MainWindow.height())
        MainWindow.setWindowFlags(Qt.CustomizeWindowHint | Qt.WindowMinimizeButtonHint)  # 禁用右上角关闭
        MainWindow.setWindowTitle(f"数据库导出XLSX软件   {Version}")
        self.MainWindow = MainWindow

        self.timer = None
        self.percents_min, self.percents_max = 0, 100

        self.labelHint(HintText, "rgb(21,255,79)", 0, HintBlink)

        # 设置占位符标签的stretch因子
        spacer_left = QLabel()
        self.horizontalLayout_1.addWidget(spacer_left, stretch=1)

        self.ip_labels = []
        self.ip_edits = []
        width, height = 40, 30
        font_size = 16
        default_ip = ["192", "168", "1", "100"]
        # default_ip = ["192", "168", "3", "112"]
        # Add four QLineEdit widgets for each octet of the IP address
        for i in range(4):
            label = QLabel(f".")
            # label.setStyleSheet("color:rgb(255, 255, 255)")
            label.setMinimumHeight(height)
            label.setStyleSheet(f"QLabel {{ font-size: {font_size}px; color:rgb(255, 255, 255) }}")

            edit = QLineEdit(MainWindow)
            edit.setMaxLength(3)  # Each segment should have up to 3 digits
            edit.setMinimumHeight(height)
            edit.setMaximumWidth(width)
            edit.setStyleSheet(f"QLineEdit {{ font-size: {font_size}px; }}")

            edit.setPlaceholderText(default_ip[i])
            edit.setText(default_ip[i])

            # Regular expression for validating an IP address segment (0-255)
            regexp = QRegExp(r"^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$")
            validator = QRegExpValidator(regexp, edit)
            edit.setValidator(validator)

            self.ip_labels.append(label)
            self.ip_edits.append(edit)

            self.horizontalLayout_1.addWidget(label)
            self.horizontalLayout_1.addWidget(edit)

        self.ip_labels[0].setText("设备IP地址:")
        
        # 设置占位符标签的stretch因子
        spacer_right = QLabel()
        self.horizontalLayout_1.addWidget(spacer_right, stretch=1)

        # # Connect the editingFinished signal of each QLineEdit to a slot for validation
        # for edit in self.ip_edits:
        #     edit.editingFinished.connect(self.validate_ip)

        # 进度条
        self.progressBar.hide()

        self.progressBar_signal.connect(self.updateProcess)
        self.progressBar_show_signal.connect(self.progressBarShow)
        self.button_ctrl_signal.connect(self.buttonCtrl)
        self.dialog_signal.connect(self.dialogShow)
        self.set_address_signal.connect(self.setAddress)
        self.label_hint_signal.connect(self.labelHint)

        self.pushButton_scan.clicked.connect(self.scanClicked)
        self.pushButton_export.clicked.connect(self.exportClicked)
        self.pushButton_exit.clicked.connect(self.exitClicked)

        self.pushButton_export.setFocus()

        # self.pushButton_export.clicked.emit()  # ssjhs: 调试用, 开机触发按键

    def __labelHintBlink(self):
        self.count -= 1

        if self.count % 2 == 0:
            self.label_hint.show()
        else:
            self.label_hint.hide()

        if self.count == 0:
            self.timer.stop()
            self.timer = None

    def labelHint(self, text, color, count, interval):
        if self.timer is not None:
            self.timer.stop()
            self.timer = None

        self.label_hint.show()
        self.label_hint.setText(text)
        self.label_hint.setStyleSheet(f"color: {color};")
        if count > 0 and interval > 0:
            self.count = count * 2
            self.timer = QTimer(self)
            self.timer.timeout.connect(self.__labelHintBlink)
            self.timer.start(round(interval * 1000))

    def updateProcess(self, percent):
        self.progressBar.setValue(round(percent))

    def progressBarShow(self, is_show):
        if is_show:
            self.progressBar.show()
        else:
            self.progressBar.hide()

    def __progress_bar(self, transferred, toBeTransferred, suffix=""):
        try:
            bar_len = 100
            filled_len = int(round(bar_len * transferred / float(toBeTransferred)))
            percents = round(100.0 * transferred / float(toBeTransferred), 1)

            # bar = "\033[32;1m%s\033[0m" % "=" * filled_len + "-" * (bar_len - filled_len)
            # sys.stdout.write("[%s] %s%s %s\r" % (bar, "\033[32;1m%s\033[0m" % percents, "%", suffix))
            # sys.stdout.flush()

            self.progressBar_signal.emit(percents)
        except Exception as e:
            print(f"__progress_bar: {e} {toBeTransferred}")

    def __progress_bar_download(self, transferred, toBeTransferred, suffix=""):
        try:
            bar_len = 100
            filled_len = int(round(bar_len * transferred / float(toBeTransferred)))
            percents = round(100.0 * transferred / float(toBeTransferred), 1)
            percents = self.percents_min + percents * (self.percents_max - self.percents_min) / 100
            percents = round(percents, 1)

            # bar = "\033[32;1m%s\033[0m" % "=" * filled_len + "-" * (bar_len - filled_len)
            # sys.stdout.write("[%s] %s%s %s\r" % (bar, "\033[32;1m%s\033[0m" % percents, "%", suffix))
            # sys.stdout.flush()

            self.progressBar_signal.emit(percents)
        except Exception as e:
            print(f"__progress_bar_download: {e} {toBeTransferred}")

    def dialogShow(self, text_list):
        dialog = CustomDialog(text_list, self)
        dialog.exec_()

    def setAddress(self, address):
        text = address.split(".")
        for i, edit in enumerate(self.ip_edits):
            self.ip_edits[i].setText(text[i])

    def scanClicked(self):
        self.buttonCtrl(False)
        self.thread = threading.Thread(target=self.scanThread, daemon=True)
        self.thread.start()

    def exportClicked(self):
        self.buttonCtrl(False)
        self.thread = threading.Thread(target=self.exportThread, daemon=True)
        self.thread.start()

    def exitClicked(self):
        self.MainWindow.close()

    def buttonCtrl(self, v):
        self.pushButton_exit.setEnabled(v)
        self.pushButton_scan.setEnabled(v)
        self.pushButton_export.setEnabled(v)
        for ip_edit in self.ip_edits:
            ip_edit.setEnabled(v)

    def scanThread(self):
        print("start scaning")
        self.label_hint_signal.emit("开始扫描局域网所有设备端口\nETA 8秒, 请稍等", "rgb(21,255,79)", 0, HintBlink)

        local_addresses = get_all_local_ip()
        target_addresses = scan_all_ssh_port(local_addresses)
        # target_addresses = ["192.168.3.112", "192.168.3.113"]  # ssjhs debug
        if len(target_addresses) == 0:
            self.label_hint_signal.emit("未找到设备", "rgb(255, 0, 0)", 3, HintBlink)
        else:
            self.dialog_signal.emit(target_addresses)
            self.set_address_signal.emit(target_addresses[0])  # 默认填入第一个设备的IP地址
            self.label_hint_signal.emit("扫描完成", "rgb(21,255,79)", 3, HintBlink)

        self.button_ctrl_signal.emit(True)
        print("scaning finished")

    def validate_ip(self):
        segments = [edit.text() for edit in self.ip_edits]
        # Check if all segments are filled and valid
        ip_address = ""
        try:
            if not all(segments):
                raise Exception("ip error")
            ip_address = ".".join(segments)
        except (ValueError, Exception) as e:
            print("\033[1;31m" + str(e) + "\033[0m")

        return ip_address

    def exportThread(self):
        from Tools.sshClient import SshClient
        from Tools.sqlite2xlsx import get_data_from_sqlite, format_xlsx, format_dataframe

        remote_database_file = f"/home/pi/app/{DatabaseFile}"
        local_file = f"export.xlsx"

        t0 = time.time()
        try:
            print("start exporting")
            self.progressBar_show_signal.emit(True)
            self.progressBar_signal.emit(0)

            # ip地址检查
            ip_address = self.validate_ip()
            self.label_hint_signal.emit("检查IP地址", "rgb(21,255,79)", 0, HintBlink)
            if len(ip_address) == 0:
                raise Exception("IP地址填写错误")

            if True:
                # 初始化SSH客户端口
                self.label_hint_signal.emit(f"初始化端口({ip_address})", "rgb(21,255,79)", 0, HintBlink)

                ssh = SshClient()
                ssh.ssh_login(ip_address, "pi", "raspberry")

                # 进度条刷新
                self.progressBar_signal.emit(5)
                time.sleep(0.001)  # 出让CPU控制权, 让主线程刷新进度条

                # 下载数据库文件
                self.label_hint_signal.emit("下载数据库文件", "rgb(21,255,79)", 0, HintBlink)
                self.percents_min, self.percents_max = self.progressBar.value(), 20
                ssh.download_file_with_progress(remote_database_file, f"{DownloadPath}", self.__progress_bar_download)

                # 关闭SSH客户端口
                ssh.ssh_logout()

            # 解释数据库文件

            # raise Exception("调试退出-ssjhs")
            database_file = os.path.join(DownloadPath, DatabaseFile)
            df = get_data_from_sqlite(database_file)
            df = format_dataframe(df)
            df.to_excel(local_file)
            format_xlsx(local_file)

            self.progressBar_signal.emit(100)
            self.label_hint_signal.emit("导出成功", "rgb(21,255,79)", 3, HintBlink)

        except Exception as e:
            print("\033[1;31m" + str(e) + "\033[0m")
            self.label_hint_signal.emit(str(e), "rgb(255,50,50)", 3, HintBlink)

        self.buttonCtrl(True)
        self.progressBar_show_signal.emit(False)
        print("exporting finished")


##############################################################################
import os
import sys

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QApplication
from UICustom.home import Ui_MainWindow


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
